<script lang="ts" setup>
import type { PropType } from "vue";
import { request } from "@/utils/request";
import { debounceUtils } from "@/utils/index.js";
import { deepClone } from "@/utils";

type Props = {
  label: string;
  key: string;
  disabled: string;
};

const props = defineProps({
  data: {
    type: Array,
    default: () => [],
  },
  seletced: {
    type: Array,
    default: () => [],
  },
  titles: {
    type: Array as PropType<any> as PropType<[string, string]>,
    default: () => [],
  },
  loadUrl: {
    type: String,
  },
  props: {
    type: Object as PropType<Props>,
    default: () => ({
      label: "name",
      key: "id",
      disabled: "disabled",
    }),
  },
  hiddenDisabled: {
    type: Boolean,
    default: false,
  },
});

const sourceTotal = ref(0),
  totalResult = ref(0),
  pager = ref({ currentPage: 1, showCount: 15 }),
  leftSearch = ref(""),
  rightSearch = ref(""),
  originalSourceList = ref([]),
  sourceList = ref<Array<any>>([]),
  targetlist = ref<Array<any>>([]),
  disabled = ref(false),
  leftChecked = ref({}),
  rightChecked = ref({}),
  defaultSeletced = ref<Array<any>>([]);

watch(
  () => props.seletced,
  (val) => {
    console.log("watch props", val);
    nextTick(() => {
      defaultSeletced.value = deepClone(val);
      targetlist.value = [];
      sourceList.value = formatData(originalSourceList.value);
    });
  },
  { deep: true, immediate: true }
);

const leftPanelTitle = computed(() => props.titles[0] || "Source");
const rightPanelTitle = computed(() => props.titles[1] || "Target");
const leftBtnDisabled = computed(
  () => Object.getOwnPropertyNames(rightChecked.value).length === 0
);
const rightBtnDisabled = computed(
  () => Object.getOwnPropertyNames(leftChecked.value).length === 0
);
const sourceSelected = computed(
  () => Object.getOwnPropertyNames(leftChecked.value).length
);
const targetSelected = computed(
  () => Object.getOwnPropertyNames(rightChecked.value).length
);
const targetResultList = computed(() => {
  if (rightSearch.value) {
    return targetlist.value.filter(
      (item) => item[props.props.label].indexOf(rightSearch.value) >= 0
    );
  } else return targetlist.value;
});
const filter = computed(() => ({
  ...pager.value,
  name: leftSearch.value,
}));

const formatData = (list: Array<any>) => {
  let sourceList = list.slice(0);
  for (let i = 0; i < defaultSeletced.value.length; i++) {
    let item = defaultSeletced.value[i];
    if (item instanceof Object) {
      if (!item[props.props.disabled]) {
        let exit = targetlist.value.find(
          (val) => val[props.props.key] == item[props.props.key]
        );
        if (!exit) targetlist.value.push(item);
      }

      let index = sourceList.findIndex(
        (source) => source[props.props.key] == item[props.props.key]
      );

      if (index >= 0) {
        if (!item[props.props.disabled]) sourceList.splice(index, 1);
        else sourceList[index][props.props.disabled] = true;
        defaultSeletced.value.splice(i, 1);
        i--;
      }
    } else {
      let target = originalSourceList.value.find(
        (original) => original[props.props.key] == item[props.props.key]
      );
      if (target) {
        let exit = targetlist.value.find((val) => val[props.props.key] == item);
        if (!exit) targetlist.value.push(target);

        let index = sourceList.findIndex(
          (source) => source[props.props.key] == item
        );
        if (index >= 0) {
          sourceList.splice(index, 1);
          defaultSeletced.value.splice(i, 1);
          i--;
        }
      }
    }
  }
  return sourceList;
};

const load = async () => {
  if (!props.loadUrl) {
    sourceList.value = formatData(props.data);
    disabled.value = true;
    return;
  }
  const rs: any = await request(props.loadUrl, filter.value);
  if (rs.code == 200) {
    if (rs.page.currentPage == 1) {
      totalResult.value = rs.page.totalResult;
      sourceTotal.value = rs.page.totalResult;
      originalSourceList.value = [];
      sourceList.value = [];
    }
    if (rs.page.currentPage == rs.page.totalPage) disabled.value = true;
    let formatList = formatData(rs.payload);
    sourceList.value = sourceList.value.concat(formatList);
    originalSourceList.value = originalSourceList.value.concat(rs.payload);
    pager.value.currentPage++;
  }
};

const toLeft = () => {
  for (let i = 0; i < targetlist.value.length; i++) {
    let item = targetlist.value[i];
    if (rightChecked.value[item[props.props.key]]) {
      sourceList.value.unshift(item);
      rightChecked.value[item[props.props.key]] = false;
      targetlist.value.splice(i, 1);
      i--;
    }
  }
  sourceTotal.value = totalResult.value - targetlist.value.length;
  rightChecked.value = {};
};

const toRight = () => {
  for (let i = 0; i < sourceList.value.length; i++) {
    let item = sourceList.value[i];
    if (leftChecked.value[item[props.props.key]]) {
      targetlist.value.push(item);
      leftChecked.value[item[props.props.key]] = false;
      sourceList.value.splice(i, 1);
      i--;
    }
  }
  sourceTotal.value = totalResult.value - targetlist.value.length;
  leftChecked.value = {};
};

const getKeys = () => {
  let list = deepClone(toRaw(targetlist.value));
  return list;
};

const changeSourceSearch = debounceUtils(() => {
  pager.value.currentPage = 1;
  disabled.value = false;
  defaultSeletced.value = deepClone(props.seletced.concat(targetlist.value));
  load();
}, 500);

onMounted(async () => {
  console.log("onmounted", props.seletced);
  defaultSeletced.value = deepClone(props.seletced);
  await load();
});

defineExpose({ getKeys });
</script>
<template>
  <div class="transfer-container">
    <div class="transfer-panel">
      <div class="transfer-panel_header">
        {{ leftPanelTitle }}
        <span class="transfer-panel_header-total"
          >{{ sourceSelected }}/{{ sourceTotal }}</span
        >
      </div>
      <div class="transfer-panel_search">
        <el-input
          v-model="leftSearch"
          placeholder="请输入搜索内容"
          clearable
          @input="changeSourceSearch"
        />
      </div>
      <div class="transfer-panel_main">
        <ul
          v-infinite-scroll="load"
          :infinite-scroll-disabled="disabled"
          :infinite-scroll-immediate="false"
          class="infinite-list"
          style="overflow: auto"
        >
          <li
            v-for="item in sourceList"
            :key="item[props.props.key]"
            class="infinite-list-item"
          >
            <el-checkbox
              v-model="leftChecked[item[props.props.key]]"
              :label="item[props.props.label]"
              :disabled="item[props.props.disabled]"
            />
          </li>
        </ul>
      </div>
    </div>
    <div class="transfer-btn">
      <el-button type="primary" :disabled="leftBtnDisabled" @click="toLeft"
        ><el-icon><arrow-left /></el-icon
      ></el-button>
      <el-button type="primary" :disabled="rightBtnDisabled" @click="toRight"
        ><el-icon><arrow-right /></el-icon
      ></el-button>
    </div>
    <div class="transfer-panel">
      <div class="transfer-panel_header">
        {{ rightPanelTitle }}
        <span class="transfer-panel_header-total"
          >{{ targetSelected }}/{{ targetlist.length }}</span
        >
      </div>
      <div class="transfer-panel_search">
        <el-input
          v-model="rightSearch"
          placeholder="请输入搜索内容"
          clearable
        />
      </div>
      <div class="transfer-panel_main">
        <ul class="infinite-list" style="overflow: auto">
          <template
            v-for="item in targetResultList"
            :key="item[props.props.key]"
          >
            <li class="infinite-list-item">
              <el-checkbox
                v-model="rightChecked[item[props.props.key]]"
                :label="item[props.props.label]"
                :disabled="item[props.props.disabled]"
              />
            </li>
          </template>
        </ul>
      </div>
    </div>
  </div>
</template>
<style lang="scss" scoped>
.transfer-container {
  display: inline-flex;
  justify-content: space-between;
  align-items: center;
  .transfer-panel {
    border: 1px solid #ebeef5;
    min-width: 200px;
    &_header {
      border-bottom: 1px solid #ebeef5;
      padding: 10px 20px;
      &-total {
        float: right;
      }
    }
    &_search {
      margin: 10px;
    }
    &_main {
      height: 300px;
      overflow: auto;
      .infinite-list {
        height: 100%;
        &-item {
          padding: 0 20px;
        }
      }
    }
  }
  .transfer-btn {
    margin: 0 20px;
    display: flex;
    flex-wrap: wrap;
    .el-button {
      margin: 10px;
      + .el-button {
        margin: 10px;
      }
    }
  }
}
</style>
